﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Intelligent.OTC.Common.UnitOfWork;
using Intelligent.OTC.Domain.DataModel;
using Intelligent.OTC.Domain.Repositories;
using System.Configuration;
using System.IO;
using Intelligent.OTC.Domain;
using Intelligent.OTC.Common;
using System.Data.Entity.Validation;
using Intelligent.OTC.Common.Utils;
using Intelligent.OTC.Common.Exceptions;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Web;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Services;
using System.Threading;
using System.Net.Mail;
using MimeKit;
using Google.Apis.Upload;
using Intelligent.OTC.Domain.Proxy.Workflow;
using Google.Apis.Util.Store;
using System.Transactions;
using EntityFramework.MappingAPI.Mappings;
using EntityFramework.MappingAPI.Exceptions;
using EntityFramework.BulkInsert.Extensions;
using Spring.Expressions;
using MIME = Intelligent.OTC.Common.MIME;
using System.Collections;
using System.Data.Entity;

namespace Intelligent.OTC.Business
{
    public class MailService : IMailService
    {
        private string IsMail = ConfigurationManager.AppSettings["IsMail"].ToString();
        public OTCRepository CommonRep { get; set; }
        public IMailer Mailer { get; set; }

        public IQueryable<MailTemplate> GetMailTemplates()
        {
            return CommonRep.GetQueryable<MailTemplate>();
        }

        public MailTemplate GetMailTemplateById(int templateId)
        {
            return CommonRep.FindBy<MailTemplate>(templateId);
        }

        public MailTemplate GetMailTemplate(string type)
        {
            var tpl = (from t in CommonRep.GetQueryable<MailTemplate>()
                       where t.Type == type
                       select t).FirstOrDefault();
            return tpl;
        }

        /// <summary>
        /// All mail in OTC platform is going through the process of creation from template.
        /// Defaultly, This method use the template passed in and create mail instance by taking the registed context parameters and calculate it towards the template.
        /// The output of this method is a new mail instance created from the ParseTemplate method.
        /// </summary>
        /// <param name="template"></param>
        /// <param name="registContextCallBack"></param>
        /// <returns></returns>
        public MailTmp GetInstanceFromTemplate(MailTemplate template, Action<ITemplateParser> registContextCallBack)
        {
            // 1, create the mail instance.
            MailTmp ins = new MailTmp();
            ins.From = AppContext.Current.User.Email;
            ins.Subject = template.Subject;
            ins.MessageId = Guid.NewGuid().ToString();
            ins.Type = "OUT";
            ins.Category = "Draft";

            // 2, regist outer references.
            string insBody = string.Empty;
            ITemplateParser parser = new TemplateParser();
            if (registContextCallBack !=null)
            {
                registContextCallBack(parser);
            }

            // by default regist common context.
            parser.RegistContext("mailContext", new MailTemplateContext() { Parser = parser, CommonRep = this.CommonRep });
            parser.RegistContext("appContext.CurrentUser", AppContext.Current.User);
            
            // 3, purse the body
            parser.ParseTemplate(template.MainBody, out insBody);
            ins.Body = insBody;
            return ins;
        }

        public void MarkAsRead(string mailBox, string msgId)
        {
            Mailer.MarkAsRead(mailBox, msgId);
        }

        public MailTmp SendMail(MailTmp mail)
        {
            //// 1, write contact history
            //ContactService cs = SpringFactory.GetObjectImpl<ContactService>("ContactService");
            //string[] cus;
            //if (!string.IsNullOrEmpty(mail.Bussiness_Reference))
            //{
            //    cus = mail.Bussiness_Reference.Split(',');
            //}
            //else
            //{
            //    cus = "0".Split(',');
            //}
            //var ConHisList = cs.AddContactHistoryList(cus, mail.To, mail.MessageId, contactHis =>
            //{
            //    contactHis.ContactType = "Mail";
            //});

            bool isMail = false;
            if (bool.TryParse(ConfigurationManager.AppSettings["IsMail"], out isMail) && isMail)
            {
                string WorkEnv = ConfigurationManager.AppSettings["WorkEnv"];
                if (!string.IsNullOrEmpty(WorkEnv))
                {
                    mail.Subject = WorkEnv + mail.Subject;
                }

                // 2, send mail
                innerSendMail(mail);
            }
            mail.Category = "Sent";

            // 3, save mail
            //T_MAIL
            return SaveMail(mail);
            //saveSendMail(mailList);

            //if (ConHisList.Count > 0)
            //{
            //    //CONTACT_HIS
            //    (CommonRep.GetDBContext() as OTCEntities).BulkInsert(ConHisList);
            //}

        }

        //public void SendMailWithOutSave(MailTmp mail)
        //{
        //    // 1, send mail
        //    innerSendMail(mail);

        //    if ("draftSend".Equals(mail.Category))//draft send
        //    {
        //        //if (result == 1)// send mail and save mail info is sucessful
        //        //{
        //        deleteMail(mail.Id);
        //        //}//delete draft
        //    }
        //}

        private void innerSendMail(MailTmp mailInstance)
        {
            #region
            //// 
            //if (mailInstance.Attachments !=null && mailInstance.Attachments.Count> 0)
            //{ 
            //    // get template folder path + attachment file name
            //    //mailInstance.Attachments.ForEach(attach=> HttpContext.Current.Server.MapPath(
            //}

            //// parameters
            //var clientId = ConfigurationManager.AppSettings["GmailClientId"];
            //var clientSecret = ConfigurationManager.AppSettings["GmailClientSecret"];
            //var senderName = ConfigurationManager.AppSettings["EmailSenderName"];
            //var receiverName = mailInstance.To.Substring(0, mailInstance.To.IndexOf("@"));
            //var receiverAddress = mailInstance.To.ToString();
            //string subject = mailInstance.Subject.ToString();
            //string body = mailInstance.Body.ToString();
            //string fileDir = mailInstance.Attachments[0].ToString();
             


            //// Send mail by GMail api.
            //try {
            //    //OAuth2 Auth
            //    var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
            //            new ClientSecrets
            //            {
            //                ClientId = clientId,
            //                ClientSecret = clientSecret,
            //            },
            //            new[] { GmailService.Scope.GmailCompose },
            //            "user",
            //            CancellationToken.None).Result;
                
            //    var service = new GmailService(new BaseClientService.Initializer
            //    {
            //        HttpClientInitializer = credential,
            //        ApplicationName = "Gmail API",
            //    });
            //    //var upload = new  Google.Apis.Upload.ResumableUpload
            //    // format the message
            //    var text = string.Format("From: {0}\nTo: {1} <{2}>\nSubject: {3}\n\n{4}\nattach{5}",
            //        senderName,
            //        //senderAddress,
            //        receiverName,
            //        receiverAddress,
            //        subject,
            //        body,
            //        fileDir);
            //    text = text.Replace("+", "-");
            //    text = text.Replace("/", "_");
            //    text = text.Replace("*", "");

            //    // must be base64 encoded but also web safe and also initially UTF8
            //    var encodedText = System.Web.HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(text));
            //    var message = new Message { Raw = encodedText  }; 
            //    var request = service.Users.Messages.Send(message, "me").Execute();
            //    //Google.Apis.Upload.ResumableUpload<
            //}
            //catch (Exception ex){
            //    throw new Exception(ex.Message);
            //}
            #endregion
            try
            {
                AssertUtils.ArgumentHasText(mailInstance.From, "From address");

                if (!Mailer.CheckMailBoxInitialized(AppContext.Current.User.Email))
                {
                    string errMsg = string.Format("User: {0} mail box: {1} havn't initialized, Please complete the initialization in 'User initialization' page", AppContext.Current.User.EID, AppContext.Current.User.Email);
                    throw new OTCServiceException("errMsg");
                }

                // must be base64 encoded but also web safe and also initially UTF8
                //var encodedText = System.Web.HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(mail));
                var message = new Message { Raw = getRawMessage(mailInstance) };

                Mailer.SendMailRaw(new MailRaw() { MailBox = mailInstance.From, RawMsg = message.Raw });
            }
            catch (Exception ex)
            {
                Helper.Log.Error(ex.Message, ex);
                throw new MailServiceException(ex);
            }
            
            //var message = MimeMessage.CreateFromMailMessage(mail).ToString();
            //message = message.Replace("+", "-");
            //message = message.Replace("/", "_");
            //message = message.Replace("*", "");
            //var encodedText = System.Web.HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(message));
            //    var sss = new Message { Raw = encodedText  };
            //    var request = service.Users.Messages.Send(sss, "me").Execute();
            //SmtpServer.Port = 587;
            //SmtpServer.Credentials = new System.Net.NetworkCredential("nicholasping2015", "nick851001");
            //SmtpServer.EnableSsl = true;

            //SmtpServer.Send(mail);
            
        }

        public string getRawMessageWithAttachment(MailTmp mailInstance)
        {
            FileService fs = SpringFactory.GetObjectImpl<FileService>("FileService");
            var msg = new MailMessage();
            msg.Subject = mailInstance.Subject;
            if (!string.IsNullOrEmpty(mailInstance.Cc))
            {
                string[] mailcc = mailInstance.Cc.Split(';');
                foreach (var cc in mailcc)
                {
                    var dist = Helper.AddressToDistributor(cc);
                    msg.CC.Add(new MailAddress(dist.Address, Helper.GetMsgDisplayName(dist.DisplayName)));
                }
            }
            
            string[] mailto = mailInstance.To.Split(';');
            foreach (var mt in mailto)
            {
                var dist = Helper.AddressToDistributor(mt);
                msg.To.Add(new MailAddress(dist.Address, Helper.GetMsgDisplayName(dist.DisplayName)));
            }

            msg.From = new MailAddress(mailInstance.From, Helper.GetMsgDisplayName(mailInstance.DisplayName));
            msg.BodyEncoding = Encoding.UTF8;
            msg.Body = mailInstance.Body;

            msg.IsBodyHtml = true;

            //MimeMessage m = new MimeMessage();
            //m.From.Add(new MailboxAddress("", msg.From.ToString()));
            //m.To.Add(new MailboxAddress("", msg.To.ToString()));
            //m.Subject = msg.Subject;
            //m.MimeVersion = new Version("1.0");
            //m.Cc.Add(new MailboxAddress("",msg.CC.ToString()));
            //var multipart = new Multipart("mixed");
            //var body = new TextPart("html", "")
            //{
            //    Text = ""
            //};
            if (mailInstance.Attachments != null && mailInstance.Attachments.Count > 0)
            {
                List<AppFile> appFiels = fs.GetAppFiles(mailInstance.Attachments);

                foreach (var f in appFiels)
                {
                    msg.Attachments.Add(new Attachment(File.OpenRead(f.PhysicalPath), new System.Net.Mime.ContentType("utf-8")));

                    //var attachment = new MimePart("multipart/related");//application/txt

                    //attachment.ContentObject = new ContentObject(File.OpenRead(f.PhysicalPath), ContentEncoding.Default);
                    //attachment.ContentDisposition = new ContentDisposition(ContentDisposition.Attachment);
                    //attachment.ContentTransferEncoding = ContentEncoding.Base64;
                    //attachment.FileName = f.FileName;
                    //multipart.Add(attachment);
                }
            }
            //multipart.Add(body);
            //m.Body = multipart;
            //string n = m.ToString();
            //int IndexStart = n.IndexOf("boundary");
            //string Boundary = n.Substring(IndexStart +"boundary".Length + 2, 26);
            //int IndexEnd = n.LastIndexOf(Boundary) - 2;
            //string newn = n.Insert(IndexEnd, msg.Body + "\r\n");

            string newn = msg.ToString();

            // add into file table
            OTCTimerRepository TimerRep = new OTCTimerRepository() { UOW = new EFUnitOfWork() };
            fs = new FileService() { CommonRep = TimerRep };
            MemoryStream ms = new MemoryStream(newn.Length);
            StreamWriter sw = new StreamWriter(ms);
            sw.Write(newn);
            sw.Flush();
            ms.Position = 0;
            AppFile file = fs.AddAppFile(mailInstance.Subject + ".eml", ms, FileType.SentMail);
            //fileid need to save in mail table
            mailInstance.FileId = file.FileId;
            return getBaseUrlEncoded(newn);
        }

        private string getRawMessage(MailTmp mailInstance)
        {
            FileService fs = SpringFactory.GetObjectImpl<FileService>("FileService");
            var msg = new MailMessage();
            msg.Subject = mailInstance.Subject;
            if (!string.IsNullOrEmpty(mailInstance.Cc))
            {
                string[] mailcc = mailInstance.Cc.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var cc in mailcc)
                {
                    var dist = Helper.AddressToDistributor(cc);
                    msg.CC.Add(new MailAddress(dist.Address, Helper.GetMsgDisplayName(dist.DisplayName)));
                }
            }

            string[] mailto = mailInstance.To.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var mt in mailto)
            {
                var dist = Helper.AddressToDistributor(mt);
                msg.To.Add(new MailAddress(dist.Address, Helper.GetMsgDisplayName(dist.DisplayName)));
            }

            msg.From = new MailAddress(mailInstance.From, Helper.GetMsgDisplayName(mailInstance.DisplayName));
            //msg.BodyEncoding = Encoding.UTF8;
            msg.IsBodyHtml = true;

            MimeMessage mimeMsg = MimeMessage.CreateFromMailMessage(msg);
            var multipart = new Multipart("mixed");
            var body = new TextPart("html")
            {
                Text = mailInstance.Body,
                ContentTransferEncoding = ContentEncoding.Base64
            };

            multipart.Add(body);

            if (mailInstance.Attachments != null && mailInstance.Attachments.Count > 0)
            {
                List<AppFile> appFiels = fs.GetAppFiles(mailInstance.Attachments);

                foreach (var f in appFiels)
                {
                    var attachment = new MimePart("application/octet-stream");
                    attachment.ContentObject = new ContentObject(File.OpenRead(f.PhysicalPath), ContentEncoding.Default);
                    attachment.ContentDisposition = new ContentDisposition(ContentDisposition.Attachment);
                    attachment.ContentTransferEncoding = ContentEncoding.Base64;
                    attachment.FileName = f.FileName;
                    multipart.Add(attachment);
                }
            }

            mimeMsg.Body = multipart;
            string newn = mimeMsg.ToString();

            // add into file table
            OTCTimerRepository TimerRep = new OTCTimerRepository() { UOW = new EFUnitOfWork() };
            fs = new FileService() { CommonRep = TimerRep };
            MemoryStream ms = new MemoryStream(newn.Length);
            StreamWriter sw = new StreamWriter(ms);
            sw.Write(newn);
            sw.Flush();
            ms.Position = 0;
            AppFile file = fs.AddAppFile(mailInstance.Subject + ".eml", ms, FileType.SentMail);
            //fileid need to save in mail table
            mailInstance.FileId = file.FileId;

            return getBaseUrlEncoded(newn);
        }

        private string getBaseUrlEncoded(string input)
        { 
            var inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
            // 'Special "url-safe" base64 encode.
            return Convert.ToBase64String(inputBytes).Replace("+", "-").Replace("/", "_").Replace("=", "");
            //return System.Web.HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(input));
        }

        public void SaveOrUpdateTemplate(MailTemplate template)
        {
            if (template.Id > 0)
            {
                // update
                var existing = CommonRep.FindBy<MailTemplate>(template.Id);
                ObjectHelper.CopyObjectWithUnNeed(template, existing, new string[] { "Id" });
                CommonRep.Save(existing);
            }
            else
            {
                // insert
                template.CreateDate = AppContext.Current.User.Now;
                template.Creater = AppContext.Current.User.EID;
                CommonRep.Add(template);
            }
            CommonRep.Commit();
        }

        public void ProcessDealMailBoxs(string deal)
        { 
            // get mail box for all collectors
            XcceleratorEntities wfDbContext = new XcceleratorEntities();
            //List<SysUser> users = SpringFactory.GetObjectImpl<XcceleratorService>("XcceleratorService").GetCollectionTeamMember();
            //List<SysUser> users = new List<SysUser>();
            List<SysUser> users = wfDbContext.T_USERS.Where(e => e.T_USER_EMPLOYEE.T_ORG_TEAM.TEAM_NAME == "Collection" && e.T_USER_EMPLOYEE.T_ORG_DEAL.DEAL_NAME == deal)
                                    .Select(u => new SysUser()
                                    {
                                        EID = u.USER_CODE,
                                        Name = u.T_USER_EMPLOYEE.USER_NAME,
                                        Email = u.T_USER_EMPLOYEE.USER_MAIL,
                                        Deal = u.T_USER_EMPLOYEE.T_ORG_DEAL.DEAL_NAME,
                                        DealId = u.T_USER_EMPLOYEE.DEAL_ID.ToString()
                                    }).ToList();

            Helper.Log.Info("Start to process user mail box, total: " + users.Count);

            foreach (var user in users)
            {
                if (user.Email == null)
                {
                    Helper.Log.Info(string.Format("User: {0} mail is empty", user.EID));
                    continue;
                }

                // check if the from have complete the user initialization
                if (Mailer.CheckMailBoxInitialized(user.Email))
                {
                    Helper.Log.Info(string.Format("User: {0} mail box: initialized. Start to process.", user.EID, user.Email));
                    ProcessMailBox(user.Email, deal);
                }
                else
                {
                    Helper.Log.Info(string.Format("User: {0} mail box: {1} havn't initialized, Please complete the initialization in 'User initialization' page", user.EID, user.Email));
                }
            }
        }

        /// <summary>
        /// This method will be called from Global timer
        /// </summary>
        public void ProcessMailBox(string mailBox, string deal)
        {
            OTCTimerRepository TimerRep = new OTCTimerRepository() { UOW = new EFUnitOfWork() };
            // Retrieve mail from last msg time.
            MailRaw lastMail = TimerRep.GetDbSet<MailRaw>().Where(r => r.MailBox == mailBox && r.Deal == deal).OrderByDescending(r => r.InternalTime).FirstOrDefault();
            long lastMsgTime = 0;
            if (lastMail !=null)
            {
                // UTC timestamp.
                lastMsgTime = lastMail.InternalTime.Value;
            }
            else
            {
                // if we never download any mail for this mailbox, we only seek for the mail received start from today.
                // generate the milliseconds same as gmail internal date logic.
                lastMsgTime = Convert.ToInt64(((TimeSpan)(DateTime.Now.Date.AddHours((Helper.GetRegionTimeSheft(deal)) * -1) - new DateTime(1970, 1, 1, 0, 0, 0))).TotalMilliseconds);
            }

            
            List<MailRaw> raws = new List<MailRaw>();
            try
            {
                raws = Mailer.GetMailRaw(mailBox, lastMsgTime);

                // we should not get file service instance from spring container with "SpringFactory.GetObjectImpl<>" method. 
                // As that is the only single instance we have in our container and we should not distory its CommonRep property.
                FileService fs = new FileService() { CommonRep = TimerRep };
                raws.ForEach(r =>
                {
                    string rawstring = r.RawMsg.Replace("-", "+").Replace("_", "/");
                    string mailRaw = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(rawstring));
                    MemoryStream ms = new MemoryStream(mailRaw.Length);
                    StreamWriter sw = new StreamWriter(ms);
                    sw.Write(mailRaw);
                    sw.Flush();
                    ms.Position = 0;

                    // add into file table
                    AppFile file = fs.AddAppFile(r.MessageId + ".eml", ms, FileType.ReceivedMail);

                    // add into mail raw
                    TimerRep.GetDbSet<MailRaw>().Add(new MailRaw() { 
                         FileId = file.FileId,
                         MailBox = mailBox,
                         ProcessFlg = 0,
                         InternalTime = r.InternalTime,
                         MessageId = r.MessageId,
                         Deal = deal
                         //, InternalDateTime = Helper.GetDatetimeFromMailInternalTime(r.InternalTime.Value);
                    });

                });

                TimerRep.Commit();

                // start the rest asyn processing
                ThreadPool.QueueUserWorkItem(processMail, new { mailBox, deal });
            }
            catch (Exception ex)
            {
                Helper.Log.Error(string.Format("Error happened while loading mail message from Gmail mailbox: [{0}]", mailBox), ex);
                // Return to let other mail box get chance to process.
                return;
            }

        }

        private void processMail(dynamic startObj)
        {
            try
            {
                OTCTimerRepository TimerRep = new OTCTimerRepository() { UOW = new EFUnitOfWork() };

                string mailBox = startObj.mailBox;
                string deal = startObj.deal;

                List<MailRaw> raws = new List<MailRaw>();

                raws = TimerRep.GetDbSet<MailRaw>().Where(mr => mr.ProcessFlg == 0 && mr.MailBox == mailBox && mr.Deal == deal).ToList();

                if (raws == null || raws.Count() == 0)
                {
                    Helper.Log.Info(string.Format("No message from Gmail mailbox: [{0}]", mailBox));
                    return;
                }

                FileService fs = new FileService() { CommonRep = TimerRep };
                raws.ForEach(raw =>
                {
                    raw.ProcessFlg = 1;
                    TimerRep.Commit();
                    try
                    {
                        // Get mail raw
                        AppFile f = fs.GetAppFile(raw.FileId);

                        // Process raw
                        MailTmp m = processMailRaw(raw, f.PhysicalPath);
                        m.MailBox = mailBox;
                        m.Deal = deal;
                        m.CreateTime = Helper.GetRegionNow(deal);
                        m.UpdateTime = m.CreateTime;

                        // process mail auto assignment
                        List<MailTmp> mailList = autoAssignMessage(m, deal, TimerRep);

                        // add new mails
                        TimerRep.GetDbSet<MailTmp>().AddRange(mailList);

                        // update information from the persor
                        raw.ProcessFlg = 2;
                        TimerRep.Commit();
                    }
                    catch (Exception ex)
                    {
                        // log mail raw error information and containue to process the next mail raw
                        Helper.Log.Error("Error happened while prcessing the raw mail in mail box:" + mailBox, ex);
                        raw.ProcessFlg = 3;
                        raw.ProcessError = ex.ToString();
                        TimerRep.Commit();
                    }
                });

            }
            catch (Exception ex)
            {
                Helper.Log.Error("processMail failed", ex);
            }
        }

        public void RetryAllMail(dynamic startObj)
        {
            OTCTimerRepository TimerRep = new OTCTimerRepository() { UOW = new EFUnitOfWork() };

            string mailBox = startObj.mailBox;
            string deal = startObj.deal;

            List<MailRaw> raws = new List<MailRaw>();

            raws = TimerRep.GetDbSet<MailRaw>().Where(mr => mr.ProcessFlg == 3 && mr.MailBox == mailBox && mr.Deal == deal).ToList();

            if (raws == null || raws.Count() == 0)
            {
                Helper.Log.Info(string.Format("No message from Gmail mailbox: [{0}]", mailBox));
                return;
            }

            FileService fs = new FileService() { CommonRep = TimerRep };
            raws.ForEach(raw =>
            {
                raw.ProcessFlg = 1;
                TimerRep.Commit();
                try
                {
                    // Get mail raw
                    AppFile f = fs.GetAppFile(raw.FileId);

                    // Process raw
                    MailTmp m = processMailRaw(raw, f.PhysicalPath);
                    m.MailBox = mailBox;
                    m.Deal = deal;
                    m.CreateTime = Helper.GetRegionNow(deal);
                    m.UpdateTime = m.CreateTime;

                    // process mail auto assignment
                    List<MailTmp> mailList = autoAssignMessage(m, deal, TimerRep);

                    // add new mails
                    TimerRep.GetDbSet<MailTmp>().AddRange(mailList);

                    // update information from the persor
                    raw.ProcessFlg = 2;
                    TimerRep.Commit();
                }
                catch (Exception ex)
                {
                    // log mail raw error information and containue to process the next mail raw
                    Helper.Log.Error("Error happened while prcessing the raw mail in mail box:" + mailBox, ex);
                    raw.ProcessFlg = 3;
                    raw.ProcessError = ex.ToString();
                    TimerRep.Commit();
                }
            });

        }

        public void RetryOneMail(string mailBox, string deal, string messageId)
        {
            OTCTimerRepository TimerRep = new OTCTimerRepository() { UOW = new EFUnitOfWork() };
            List<MailRaw> raws = new List<MailRaw>();

            raws = TimerRep.GetDbSet<MailRaw>().Where(mr => mr.MessageId == messageId).ToList();

            if (raws == null || raws.Count() == 0)
            {
                Helper.Log.Info(string.Format("No message with message id: {0}", messageId));
                return;
            }

            FileService fs = new FileService() { CommonRep = TimerRep };
            raws.ForEach(raw =>
            {
                raw.ProcessFlg = 1;
                TimerRep.Commit();
                try
                {
                    // Get mail raw
                    AppFile f = fs.GetAppFile(raw.FileId);

                    // Process raw
                    MailTmp m = processMailRaw(raw, f.PhysicalPath);
                    m.MailBox = mailBox;
                    m.Deal = deal;
                    m.CreateTime = Helper.GetRegionNow(deal);
                    m.UpdateTime = m.CreateTime;

                    // process mail auto assignment
                    List<MailTmp> mailList = autoAssignMessage(m, deal, TimerRep);

                    // add new mails
                    TimerRep.GetDbSet<MailTmp>().AddRange(mailList);

                    // update information from the persor
                    raw.ProcessFlg = 2;
                    TimerRep.Commit();
                }
                catch (Exception ex)
                {
                    // log mail raw error information and containue to process the next mail raw
                    Helper.Log.Error("Error happened while prcessing the raw mail in mail box:" + mailBox, ex);
                    raw.ProcessFlg = 3;
                    raw.ProcessError = ex.ToString();
                    TimerRep.Commit();
                }
            });
        }

        public MailTmp processMailRaw(MailRaw raw, string rawPhysicalPath)
        {
            // load message
            var rawStr = File.ReadAllText(rawPhysicalPath);

            return ProcessMailRaw(raw, rawStr);
        }

        public MailTmp ProcessMailRaw(MailRaw raw, string mailRaw)
        {
            MemoryStream ms = new MemoryStream();
            StreamWriter sw = new StreamWriter(ms);
            sw.Write(mailRaw);
            sw.Flush();
            ms.Position = 0;
            MimeMessage mimeMsg = null;

            try
            {
                mimeMsg = MimeMessage.Load(ms);
            }
            catch (Exception ex)
            {
                Helper.Log.Error("Failed to load message stream into MimeMessage", ex);

                // trying to read message subject and add a mail record with minimum information.
                Helper.Log.Info("trying to read message subject and add a mail record with minimum information.");
                string subject = string.Empty;
                var subIdx = mailRaw.IndexOf("Subject", StringComparison.OrdinalIgnoreCase);
                if (subIdx > 0)
                {
                    subIdx = subIdx + 8;
                    var subEndIdx = mailRaw.IndexOf("\r\n", subIdx);
                    subject = mailRaw.Substring(subIdx, subEndIdx - subIdx);
                }

                if (!string.IsNullOrEmpty(subject))
                {
                    MailTmp min = new MailTmp();
                    min.Subject = subject;
                    min.Attachment = string.Empty;
                    min.InternalTime = raw.InternalTime;
                    min.InternalDatetime = Helper.GetDatetimeFromMailInternalTime(min.InternalTime.Value);
                    min.MessageId = raw.MessageId;
                    min.Category = "Inbox";
                    min.Operator = "System";
                    min.Type = "IN";
                    return min;
                }
                else
                {
                    throw;
                }
            }

            sw.Close();

            Helper.Log.Info("Msg: " + raw.MessageId + " created");

            // Parse mail instance from mimeMsg
            MailTmp m = new MailTmp();
            m.From = mimeMsg.From.ToString();
            m.To = mimeMsg.To.ToString();
            m.Cc = mimeMsg.Cc.ToString();
            m.Subject = mimeMsg.Subject;
            if (!string.IsNullOrEmpty(mimeMsg.HtmlBody))
            {
                m.Body = mimeMsg.HtmlBody.ToString();
                m.BodyFormat = "HTML";
            }
            else
            {
                m.Body = mimeMsg.TextBody;
                m.BodyFormat = "TXT";
            }
            m.Attachment = string.Empty;
            try
            {
                processAttachments(mimeMsg, raw.MessageId, fileId =>
                {
                    m.Attachment += (fileId + ",");
                });
                processBodyParts(mimeMsg, (contentId, fileId) =>
                {
                    int inx = m.Body.IndexOf("cid:" + contentId);
                    if (inx >= 0)
                    {
                        // build the file api used for front end to reference the file resource.
                        string fileRetrivalApi = string.Format("{0}/api/appFiles?fileId={1}", ConfigurationManager.AppSettings["OTC"], fileId);

                        // replace content id reference with file api reference. 
                        m.Body = m.Body.Replace("cid:" + contentId, fileRetrivalApi);
                    }
                    else
                    {
                        Helper.Log.Info("Not found the reference to embaded content: " + "cid:" + contentId);
                    }
                });
            }
            catch (Exception ex)
            {
                Helper.Log.Error("Failed to load attachments/bodyparts from message stream.", ex);
                // If exception happens here, most 
                // swallow the exception, So that message could still be created.

                // m.Errors = "Failed to load some attachments or bodyparts from the message stream."
            }
            m.InternalTime = raw.InternalTime;
            m.InternalDatetime = Helper.GetDatetimeFromMailInternalTime(m.InternalTime.Value);
            m.MessageId = raw.MessageId;
            m.Category = "Inbox";
            m.Operator = "System";
            m.Type = "IN";

            return m;
        }

        private void processBodyParts(MimeMessage mimeMsg, Action<string, string> replaceBodyDlg)
        {
            Dictionary<string, string> res = new Dictionary<string, string>();
            OTCTimerRepository TimerRep = new OTCTimerRepository() { UOW = new EFUnitOfWork() };

            FileService fs = new FileService() { CommonRep = TimerRep };

            foreach (var item in mimeMsg.BodyParts)
            {
                // Recognize the unusual content type.
                if (item.ContentType.MediaType.ToUpper() == "TEXT" 
                    || item.IsAttachment)
                {
                    continue;
                }

                if(string.IsNullOrEmpty(item.ContentId)) 
                {
                    // Only save the content with content Id, As the content Id is unique reference which is usually referenced by text parts.
                    continue;
                }

                string contentId = item.ContentId;
                string name = item.ContentId;

                MimePart part = item as MimePart;
                if (part !=null)
                {
                    StreamReader sr = new StreamReader(part.ContentObject.Stream);
                    var partStream = sr.ReadToEnd();
                    var baseArr = Convert.FromBase64String(partStream);

                    MemoryStream tmp = new MemoryStream();
                    tmp.Write(baseArr, 0, baseArr.Length);
                    tmp.Flush();
                    tmp.Position = 0;

                    var file = fs.AddAppFile(name, tmp, FileType.MailBodyPart, contentId: contentId, contentType: part.ContentType.MimeType);

                    replaceBodyDlg(contentId, file.FileId);
                }
            }
        }

        public static List<MailTmp> autoAssignMessage(MailTmp m, string deal, OTCTimerRepository timerRep)
        {
            List<MailTmp> mailList = new List<MailTmp>();
            List<CustomerMail> custMailList = new List<CustomerMail>();
            string fromAddress = Helper.AddressToDistributor(m.From).Address;
            //string collectorEid = string.Empty;
            //collectorEid = "zhichao.wang";
            Helper.Log.Info("Trying to assign the mail to customers. From: " + fromAddress);

            XcceleratorEntities wfDbContext = new XcceleratorEntities();
            Helper.Log.Info("MailBox: " + m.MailBox);
            var empId = (from u in wfDbContext.T_USER_EMPLOYEE
                         where u.USER_MAIL == m.MailBox
                         select u.ID).FirstOrDefault();

            Helper.Log.Info("empId: " + empId);

            var user = (from u in wfDbContext.T_USERS
                        where u.USER_EMPLOYEE_ID == empId
                        select u.USER_CODE).FirstOrDefault();


            Helper.Log.Info("mailBoxcollectorEid: " + user);

            //1.1,get the customer by fromAddress
            //########### update by pxc 20160331 check customer Contactor && Group Contactor ############ s
            //@@@@@ get customer contactors union get group contactors
            var customerNums = ((from con in timerRep.GetDbSet<Contactor>()
                                 where con.EmailAddress == fromAddress && con.Deal == deal && con.IsDefaultFlg == "1"
                                 && (con.GroupCode == "" || con.GroupCode == null)
                                 select con.CustomerNum)
                                .Union(
                                from con in timerRep.GetDbSet<Contactor>().Where(c => c.EmailAddress == fromAddress && c.Deal == deal
                                && c.IsDefaultFlg == "1" && (c.GroupCode != "" && c.GroupCode != null))
                                 join grp in timerRep.GetDbSet<Customer>().Where(g => g.RemoveFlg == "1" && g.ExcludeFlg == "0" && g.Deal == deal) on new { BillGroupCode = con.GroupCode } equals new { grp.BillGroupCode }
                                 select grp.CustomerNum)
                                ).Distinct();

            Helper.Log.Info("customerNumCount: " + customerNums.Count());

            //########### update by pxc 20160331 check customer Contactor && Group Contactor ############ e

            CustomerMail custMail = new CustomerMail();

            if (customerNums == null || customerNums.Count() == 0)
            {
                int atIndex = fromAddress.IndexOf("@");
                string mailDomain = fromAddress.Remove(0, atIndex + 1);
                //1.2,get the customerNum from Domain by fromAdd
                customerNums = (from con in timerRep.GetDbSet<ContactorDomain>()
                                where con.MailDomain == mailDomain && con.Deal == deal
                                select con.CustomerNum).Distinct();

                Helper.Log.Info("[Domain]customerNumCount: " + customerNums.Count());
            }

            //INSERT DATE TO CUSTOMER_MAIL
            if (customerNums != null && customerNums.Count() > 0)
            {
                //2.get the customers that collector eid is mailbox eid
                var CollectorCustomerNums = (from cus in timerRep.GetDbSet<Customer>()
                                             where customerNums.Contains(cus.CustomerNum) && cus.Collector == user
                                             select cus.CustomerNum).Distinct();
                Helper.Log.Info("outLoopCollectorMapCustomerCount:" + CollectorCustomerNums.Count());

                //INSERT DATA TO  T_CUSTOMER_MAIL
                if (CollectorCustomerNums != null && CollectorCustomerNums.Count() > 0)
                {
                    foreach (var custNum in CollectorCustomerNums)
                    {
                        Helper.Log.Info("[matched]custNum:" + custNum);
                        custMail = new CustomerMail();
                        custMail.MessageId = m.MessageId;
                        custMail.CustomerNum = custNum.ToString();
                        custMailList.Add(custMail);
                        Helper.Log.Info("[matched]custMailListCount:" + custMailList.Count());
                    }
                }
            }

            //PARENT TABLE T_MAIL_TMP
            MailTmp copiedMail = new MailTmp();
            ObjectHelper.CopyObject(m, copiedMail);
            copiedMail.Category = "Inbox";
            Helper.Log.Info("copiedMail:" + copiedMail.MessageId);
            //CHILD TABLE T_CUSTOMER_MAIL
            copiedMail.CustomerMails = custMailList;
            Helper.Log.Info("custMailListCount:" + custMailList.Count());
            mailList.Add(copiedMail);
            Helper.Log.Info("mailListCount:" + mailList.Count());
            return mailList;
        }
        //private static Boolean AddRefByDomain(Mail m, string deal, OTCTimerRepository timerRep) 
        //{
        //    string fromAddress = Helper.AddressToDistributor(m.From).Address;
        //    int atIndex = fromAddress.IndexOf("@");
        //    string mailDomain = fromAddress.Remove(0, atIndex + 1);
        //    var customerNums = (from con in timerRep.GetDbSet<ContactDomain>()
        //                        where con.MailDomain == mailDomain && con.Deal == deal
        //                        select con.CustomerNum).Distinct();
           
        //    foreach (var custNum in customerNums)
        //    {
        //        List<Mail> mailList = new List<Mail>();
        //        Mail copiedMail = new Mail();
        //        ObjectHelper.CopyObject(m, copiedMail);
        //        copiedMail.Bussiness_Reference = custNum.ToString();
        //        copiedMail.CustomerAssignedFlg = "1";//matched with customer
        //        mailList.Add(copiedMail);
        //    }
        //    if (customerNums != null)
        //    {
        //        return true;
        //    }
        //    else {
        //        return false;
        //    }
        //}

        private string processAttachments(MimeMessage mimeMsg, string msgId, Action<string> attachmentCallBack)
        {
            OTCTimerRepository TimerRep = new OTCTimerRepository() { UOW = new EFUnitOfWork() };

            string attachmentFilesIds = string.Empty;
            string messageDes = string.Empty;
            Helper.Log.Info("foreach (MimeEntity item in mimeMsg.Attachments)");
            foreach (MimeEntity item in mimeMsg.Attachments)
            {
                MemoryStream attStream = null;
                Stream tmp = null;
                try
                {
                    string name = string.Empty;
                    if (item.ContentType.MediaType.ToUpper() == "MESSAGE"
                        && item.ContentType.MediaSubtype.ToUpper() == "RFC822")
                    {
                        //continue;
                        tmp = new MemoryStream();
                        if ((item as MimeKit.MessagePart).Message != null)
                        {
                            (item as MimeKit.MessagePart).Message.WriteTo(tmp);
                            tmp.Flush();
                            tmp.Position = 0;
                            name = (item as MimeKit.MessagePart).Message.Subject + ".eml";
                        }

                        attStream = tmp as MemoryStream;
                    }
                    else
                    {
                        MimePart part = item as MimePart;
                        if (part != null)
                        {
                            messageDes = string.Format("Mail message:{0} attachments: {1}, Size: {2}", msgId, part.ContentDisposition.FileName, part.ContentDisposition.Size);

                            tmp = part.ContentObject.Stream;
                            name = part.FileName;

                            StreamReader sr = new StreamReader(tmp);
                            var partStream = sr.ReadToEnd();
                            if (part.ContentObject.Encoding == ContentEncoding.Base64)
                            {
                                var baseArr = Convert.FromBase64String(partStream);

                                //TODO: need to extract the body transfer encoding which indicating the decode methods.
                                //string encoding = aMimeMessage.GetTransferEncoding();
                                //if (encoding == null)
                                //{
                                //    encoding = MIME.MimeConst.Encoding7Bit;
                                //}

                                //MIME.MimeCode aCode = MIME.MimeCodeManager.Instance.GetCode(encoding);
                                //aCode.Charset = aMimeMessage.GetCharset();
                                //byte[] b = aCode.DecodeToBytes(attachment.GetPrivateBody());

                                attStream = new MemoryStream();
                                attStream.Write(baseArr, 0, baseArr.Length);
                                attStream.Flush();
                                attStream.Position = 0;
                            }
                            else
                            {
                                if (part.ContentObject.Encoding == ContentEncoding.QuotedPrintable)
                                {
                                    //TODO: special decoder is needed. see detail: http://stackoverflow.com/questions/2226554/c-class-for-decoding-quoted-printable-encoding
                                }
                                attStream = new MemoryStream();
                                StreamWriter sw = new StreamWriter(attStream);
                                sw.Write(partStream);
                                sw.Flush();
                                attStream.Position = 0;
                            }
                        }
                    }

                    FileService fs = new FileService() { CommonRep = TimerRep };

                    var file = fs.AddAppFile(name, attStream, FileType.MailAttachment);

                    attachmentCallBack(file.FileId);
                }
                catch (Exception ex)
                {
                    Helper.Log.Error("Error happened while processing the attachment. ", ex);
                    Helper.Log.Info(messageDes);
                    throw;
                }
                finally
                {
                    attStream.Close();
                    tmp.Close();
                }
            }

            if (!string.IsNullOrEmpty(attachmentFilesIds))
            {
                attachmentFilesIds = attachmentFilesIds.TrimEnd(',');
            }

            return attachmentFilesIds;
        }

        protected class TemplateParser : ITemplateParser
        {
            Dictionary<string, object> objectRegister = new Dictionary<string, object>();

            public void RegistContext(string objkey, object contextObj)
            {
                if (!objectRegister.ContainsKey(objkey))
                {
                    objectRegister.Add(objkey, contextObj);
                }
            }

            public void ParseTemplate(string template, out string templateInstance)
            { 
                // gether tokens within template
                IBaseDataService bdService = SpringFactory.GetObjectImpl<IBaseDataService>("BaseDataService");
                List<SysTypeDetail> tokens = bdService.GetSysTypeDetail("011").ToList();

                string regexFormat = @"~[\w]+~";
                MatchCollection matches = Regex.Matches(template, regexFormat);

                templateInstance = template;
                int matchIdx = 0;
                foreach (Match m in matches)
                {
                    templateInstance = templateInstance.Replace(m.Value, "{" + matchIdx + "}");
                    matchIdx++;
                }

                List<string> res = new List<string>();
                foreach (Match m in matches)
                {
                    string token = m.Value.Trim('~');
                    SysTypeDetail tokenDetail = tokens.Find(t => t.DetailName == token);
                    if (tokenDetail == null)
                    {
                        throw new OTCServiceException(string.Format("Token [{0}] cannot be parse to system invocation because there is no matching defination", tokenDetail.DetailName));
                    }

                    string tokenValue = tokenDetail.DetailValue;

                    if (tokenValue !=null)
                    {
                        Helper.Log.Info(string.Format("Token value [{0}] retrieved for token [{1}]", tokenValue, token));

                        // reflect method/property call to mail service
                        string resValue = string.Empty;
                        object resObj = new object();
                        if (objectRegister.TryGetValue(tokenDetail.DetailValue, out resObj))
                        {
                            if (!string.IsNullOrEmpty(tokenDetail.DetailValue2))
                            {
                                // If resObj is null. which means no obj to evaluate on. The expression can work on itself.
                                resValue = ExpressionEvaluator.GetValue(resObj, tokenDetail.DetailValue2) as string;
                            }
                        }
                        
                        res.Add(resValue);
                    }
                }

                // fill into template instance
                templateInstance = string.Format(templateInstance, res.ToArray());
            }

            public object GetContext(string objkey)
            {
                if (objectRegister.ContainsKey(objkey))
                {
                    object res = null;
                    if (objectRegister.TryGetValue(objkey, out res))
                    {
                        return res;
                    }
                    else
                    {
                        return null;
                    }
                }

                return null;
            }
        }
        
        public MailTmp GetMailInstance(int templateId)
        {
            return GetMailInstance(templateId, parser => { });
        }

        public MailTmp GetMailInstance(int templateId, Action<ITemplateParser> registContextDlg)
        {
            AssertUtils.IsTrue(templateId > 0);

            MailTemplate template = GetMailTemplateById(templateId);

            if (template == null)
            {
                throw new OTCServiceException("Template does not exist for id: [" + templateId + "]");
            }

            MailTmp mailInstance = GetInstanceFromTemplate(template, (parser) =>
            {
                parser.RegistContext("collector", AppContext.Current.User);

                if (registContextDlg !=null)
                {
                    registContextDlg(parser);
                }
            });

            return mailInstance;
        }

        public MailTmp GetMailInstance(MailTemplateType type, Action<ITemplateParser> registContextDlg)
        {
            MailTemplate template = GetMailTemplate(Helper.EnumToCode<MailTemplateType>(type));

            if (template == null)
            {
                throw new OTCServiceException("Template does not exist for [" + type + "] type of mail.");
            }

            MailTmp mailInstance = GetInstanceFromTemplate(template, (parser) =>
            {
                parser.RegistContext("collector", AppContext.Current.User);

                if (registContextDlg != null)
                {
                    registContextDlg(parser);
                }
            });

            return mailInstance;
        }

        /// <summary>
        /// MailDetail Use ,choose One mail record accroding Table Id to get this maildetail
        /// </summary>
        /// <param name="orginalMailId"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public MailTmp GetMailInstance(int orginalMailId, string type)
        {
            MailTmp orginalMail = (from m in CommonRep.GetQueryable<MailTmp>().Include<MailTmp, ICollection<CustomerMail>>(m => m.CustomerMails)
                                where m.Id == orginalMailId
                                select m).FirstOrDefault();

            switch (type)
            {
                case "RE":
                case "FW":
                    if (orginalMail == null)
                    {
                        throw new OTCServiceException("Orginal mail cannot be found for mail id: " + orginalMailId);
                    }

                    return getRepAndFwMail(orginalMail, type);
                case "NE":
                    return getNewMail();
                case "VI":
                    if (orginalMail == null)
                    {
                        throw new OTCServiceException("Orginal mail cannot be found for mail id: " + orginalMailId);
                    }

                    return orginalMail;
                default:
                    if (orginalMail == null)
                    {
                        throw new OTCServiceException("Orginal mail cannot be found for mail id: " + orginalMailId);
                    }

                    throw new OTCServiceException(string.Format("Not supported mail type: [{0}]!", type));
            }
        }

        //add by zhangYu customerContact REPLY/FORWARD/VIEW
        /// <summary>
        /// Get the Mail Instance (Replay/forward)
        /// </summary>
        /// <param name="id">ID of T_mail</param>
        /// <param name="type">RE:eplay / FW:Forward /VI:View</param>
        /// <returns></returns>
        private MailTmp getRepAndFwMail(MailTmp orginalMail, string type)
        {
            string orginalMailBody = getOrginalMailBody(orginalMail);

            if (type == "RE")
            {
                MailTmp newMail = GetMailInstance(MailTemplateType.Reply, (parser) =>
                {
                    parser.RegistContext("body", orginalMailBody);
                });
                newMail.Deal = AppContext.Current.User.Deal;
                newMail.Attachment = orginalMail.Attachment;
                newMail.Subject = "RE:" + orginalMail.Subject;
                newMail.To = orginalMail.From;
                newMail.Cc = orginalMail.Cc;
                newMail.CustomerMails = new List<CustomerMail>();
                foreach (var cm in orginalMail.CustomerMails)
                {
                    CustomerMail newCM = new CustomerMail()
                    {
                        CustomerNum = cm.CustomerNum,
                        MessageId = cm.MessageId
                    };

                    newMail.CustomerMails.Add(newCM);
                }
                return newMail;
            }

            if (type == "FW")
            {
                MailTmp newMail = GetMailInstance(MailTemplateType.Foward, (parser) =>
                {
                    parser.RegistContext("body", orginalMailBody);
                });
                newMail.Subject = "FW:" + orginalMail.Subject;
                newMail.CustomerMails = new List<CustomerMail>();
                foreach (var cm in orginalMail.CustomerMails)
                {
                    CustomerMail newCM = new CustomerMail()
                    {
                        CustomerNum = cm.CustomerNum,
                        MessageId = cm.MessageId
                    };

                    newMail.CustomerMails.Add(newCM);
                }
                return newMail;
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="customer"></param>
        /// <param name="language"></param>
        /// <param name="type"></param>
        /// <param name="collectorEID"></param>
        /// <param name="templateId"></param>
        /// <returns></returns>
        public MailTmp GetMailInstance(string customerNums, int templateId = 0)
        {
            MailTmp res = null;

            // 2, retrieve template based on customer information and hint.
            IMailService ms = SpringFactory.GetObjectImpl<IMailService>("MailService");
            MailTemplate tpl = null;
            if (templateId > 0)
            {
                tpl = ms.GetMailTemplateById(templateId);
            }
            else
            {
                tpl = ms.GetMailTemplate(Helper.EnumToCode<MailTemplateType>(MailTemplateType.SOA));
            }

            if (tpl != null)
            {
                res = ms.GetInstanceFromTemplate(tpl, (parser) =>
                {
                    // 1, contactNames used in SOA template
                    ContactService cs = SpringFactory.GetObjectImpl<ContactService>("ContactService");
                    IList<Contactor> contactors = cs.GetContactsByCustomers(customerNums);
                    string contactNames = string.Empty;
                    foreach (Contactor cont in contactors)
                    {
                        contactNames += (cont.Name + ", ");
                    }
                    contactNames = contactNames.TrimEnd(',');

                    parser.RegistContext("contactNames", contactNames);

                    // 2, collector
                    parser.RegistContext("collector", AppContext.Current.User);
                });

                //Add by zhangYu
                res.From = AppContext.Current.User.Email;
            }
            else
            {
                throw new OTCServiceException("No matching template was found!", System.Net.HttpStatusCode.NotFound);
            }

            return res;
        }

        /// <summary>
        /// Return a new create mail used to send out.
        /// </summary>
        /// <returns></returns>
        private MailTmp getNewMail()
        {
            var tmp = GetMailInstance(MailTemplateType.New, null);

            return tmp;
        }

        private static string getOrginalMailBody(MailTmp oldMail)
        {
            string oldMailBody = string.Empty;


            if (oldMail.BodyFormat != null && oldMail.BodyFormat.Trim().ToUpper() == "HTML")
            {
                // trim off "body"
                int start = oldMail.Body.IndexOf("<body>");
                int end = oldMail.Body.IndexOf("</body>");
                if (start > 0 && end > 0)
                {
                    oldMailBody = oldMail.Body.Substring(start + 6, end - (start + 6));
                }
                else
                {
                    oldMailBody = oldMail.Body;
                }
            }
            else if (oldMail.BodyFormat != null && oldMail.BodyFormat.Trim().ToUpper() == "TXT")
            {
                oldMailBody = oldMail.Body.Replace("\r\n", "<br/>");
            }
            else
            {
                oldMailBody = oldMail.Body;
            }

            return oldMailBody;
        }

        //added by zhangYu genarateSoa sendMail
        public void BulkSaveMail(List<MailTmp> mailInstance)
        {
            //string strContactId = "";
            //ContactHistory conHis = new ContactHistory();

            //save contact History
            //foreach (Mail ma in mailInstance)
            //{
            //    strContactId = System.Guid.NewGuid().ToString();
            //    conHis.ContactId = ma.MessageId;

            //}
            //using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 30, 0)))
            //{
            (CommonRep.GetDBContext() as OTCEntities).BulkInsert(mailInstance);
                //scope.Complete();
            //}
            //CommonRep.AddRange(mailInstance);
        }

        /// <summary>
        /// Mail can be update and save in any status no mater its type is OUT or IN.
        /// </summary>
        /// <param name="mailInstance"></param>
        public MailTmp SaveMail(MailTmp mailInstance) 
        {
            AssertUtils.ArgumentNotNull(mailInstance, "Mail Instance");
            //if(mailInstance.Bussiness_Reference !=null)
            //{mailInstance.CustomerAssignedFlg = "1";}
            if (string.IsNullOrWhiteSpace(mailInstance.MessageId))
            {
                mailInstance.MessageId = Guid.NewGuid().ToString();
            }

            if (mailInstance.Id == 0)
            {
                mailInstance.Deal = AppContext.Current.User.Deal;
                mailInstance.Operator = AppContext.Current.User.EID;
                mailInstance.Collector = AppContext.Current.User.EID;
                mailInstance.MailBox = AppContext.Current.User.Email;
                mailInstance.CreateTime = AppContext.Current.User.Now;
                mailInstance.UpdateTime = AppContext.Current.User.Now;

                CommonRep.Add<MailTmp>(mailInstance);
                CommonRep.Commit();
            }
            else
            {

                var existing = CommonRep.GetDbSet<CustomerMail>().Where(cm => cm.MessageId == mailInstance.MessageId);
                CommonRep.GetDbSet<CustomerMail>().RemoveRange(existing.AsEnumerable());
                CommonRep.Commit();

                MailTmp m = CommonRep.GetDbSet<MailTmp>().Where(t => t.Id == mailInstance.Id).Include<MailTmp, ICollection<CustomerMail>>(t => t.CustomerMails).FirstOrDefault();
                m.UpdateTime = AppContext.Current.User.Now;
                ObjectHelper.CopyObjectWithUnNeed(mailInstance, m);
                CommonRep.Commit();

                mailInstance = m;
                // foreach and do update/insert/delete
                //m.CustomerMails
            }

            return mailInstance;
        }

        public MailTmp GetMailByMessageId(string messageId)
        { 
            MailTmp ma= new MailTmp ();
            string bodyFormat = "";

            ma = CommonRep.GetQueryable<MailTmp>().Where(m => m.MessageId == messageId).FirstOrDefault();
            //Body 
            if (ma.BodyFormat != null)
            {
                bodyFormat = ma.BodyFormat.Trim();
                if (bodyFormat.ToUpper() == "HTML")
                {
                    int start = ma.Body.IndexOf("<body");
                    int index = ma.Body.IndexOf(">", start);
                    ma.Body = ma.Body.Insert(index + 1, "<br/><br/><hr width=100% size=1>");
                }
                else if (bodyFormat.ToUpper() == "TXT")
                {

                    ma.Body = "<br/><br/>" +
                                "<hr width=100% size=1>" +
                                "<br/>" + ma.Body.Replace("\r\n", "<br/>");

                }
                else
                {
                    //ma.Body
                }
            }
           
            return ma;
        }

        /// <summary>
        /// Update Selected Mail Status To Processed
        /// </summary>
        /// <param name="id"></param>
        public void UpdateMailCategory(List<int> mailIds, string category)
        {
            int[] ids = mailIds.ToArray();
            var UpdateMailSql = string.Format("");

            UpdateMailSql = string.Format(@"
                    UPDATE T_MAIL_TMP SET CATEGORY = '{1}' WHERE ID IN ({0});
                ", string.Join(",", ids), category);

            // update category
            CommonRep.GetDBContext().Database.ExecuteSqlCommand(UpdateMailSql);

            if (category == "Processed")
            {
                List<MailTmp> mails = CommonRep.GetQueryable<MailTmp>().Where(m => mailIds.Contains(m.Id)).ToList();
                if (mails.Count > 0)
                {
                    mails.ForEach(m =>
                    {
                        try
                        {
                            this.MarkAsRead(m.MailBox, m.MessageId);
                        }
                        catch (Exception ex)
                        {
                            Helper.Log.Error("MarkAsRead run failed.", ex);
                        }
                    });
                }
            }

        }

        /// <summary>
        /// assign customer and assigned_flag
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cusNum"></param>
        public void UpdateMailReferenceAndAssignedFlg(string id, string cusNum)
        {
            //MailTmp mail = CommonRep.GetQueryable<MailTmp>().Where(m => m.Id == id).FirstOrDefault();
            //mail.Bussiness_Reference = cusNum;
            //mail.CustomerAssignedFlg = "1";
            //CommonRep.Commit();
            string[] cusNums = cusNum.Split(',');
            List<CustomerMail> cmList = new List<CustomerMail>();
            foreach(var cus in cusNums){
                CustomerMail cm = new CustomerMail();
                cm.MessageId = id;
                cm.CustomerNum = cus;
                cmList.Add(cm);
            }
            CommonRep.AddRange(cmList);
            CommonRep.Commit();
             
        }

        /// <summary>
        /// added by zhangYU
        /// </summary>
        /// <param name="id"></param>
        private void deleteMail(int id)
        {
            deleteMails(new List<int>() { id });
        }

        private void deleteMails(List<int> mailIds)
        {
            var mails = CommonRep.GetQueryable<MailTmp>().Where(m => mailIds.Contains(m.Id));

            CommonRep.RemoveRange(mails);
            CommonRep.Commit();
        }


        /// <summary>
        /// added by alex
        /// Delete All Selected Mails
        /// </summary>
        /// <param name="id"></param>
        public void DeleteSelectedMail(List<int> mailIds)
        {
            int id = 0;                             //邮件id
            int count = 0;                          //count数
            string fileId = "";                     //文件id
            string[] lstAttachment;                 //附件id列表

            try
            {
                //循环mail id 进行删除
                for (int i = 0; i < mailIds.Count; i++)
                {
                    //mail id 取得
                    id = mailIds[i];
                    //mail 取得
                    MailTmp mail = CommonRep.GetQueryable<MailTmp>().Where(m => m.Id == id).FirstOrDefault();
                    //mail自动生成的message id不为空
                    if (!string.IsNullOrEmpty(mail.MessageId))
                    {
                        //contact history取得相关mail的条数
                        count = CommonRep.GetQueryable<ContactHistory>().Where(c => c.ContactId == mail.MessageId).Count();
                        if (count > 0)
                        {
                            //有相关的数据继续下一个循环
                            continue;
                        }
                        //invoice log取得相关mail的条数
                        count = CommonRep.GetQueryable<InvoiceLog>().Where(o => o.ProofId == mail.MessageId).Count();
                        if (count > 0)
                        {
                            //有相关的数据继续下一个循环
                            continue;
                        }
                        //dispute history取得相关mail的条数
                        count = CommonRep.GetQueryable<DisputeHis>().Where(d => d.EmailId == mail.MessageId).Count();
                        if (count > 0)
                        {
                            //有相关的数据继续下一个循环
                            continue;
                        }
                    }

                    //文件id取得
                    fileId = mail.FileId;
                    FileService fileService = SpringFactory.GetObjectImpl<FileService>("FileService");

                    //file id 是否为空判断
                    if (string.IsNullOrEmpty(fileId))
                    {
                        //关联 T_MAIL_RAW 表取 file id
                        MailRaw mailRow = CommonRep.GetQueryable<MailRaw>().Where(m => m.MessageId == mail.MessageId).FirstOrDefault();
                        if (mailRow != null)
                        {
                            fileId = mailRow.FileId;
                        }
                    }

                    if (!string.IsNullOrEmpty(fileId)) {
                        //删除邮件中包含的文件
                        try 
                        {
                            fileService.DeleteAppFile(fileId);
                            AppFile appFile = CommonRep.GetQueryable<AppFile>().Where(o => o.FileId == fileId).FirstOrDefault();
                            CommonRep.Remove(appFile);
                        }
                        catch (Exception e)
                        {
                            Helper.Log.Error("Delete All Selected Mail:", e);
                        } 
                    }

                    //附件id取得
                    if (!string.IsNullOrEmpty(mail.Attachment))
                    {
                        //有附件的场合
                        lstAttachment = mail.Attachment.Split(',');
                        //循环删除附件
                        for (var j = 0; j < lstAttachment.Length; j++)
                        {
                            //删除邮件中包含的所有附件
                            try
                            {
                                fileService.DeleteAppFile(lstAttachment[j]);
                                AppFile appFile = CommonRep.GetQueryable<AppFile>().Where(o => o.FileId == lstAttachment[j]).FirstOrDefault();
                                CommonRep.Remove(appFile);
                            }
                            catch (Exception e)
                            {
                                Helper.Log.Error("Delete All Selected Mail:", e);
                            } 
                            
                        }
                    }

                    //以上都没有相关数据的进行mail删除
                    // The customer mails are deleted cascade.
                    CommonRep.Remove(mail);
                    CommonRep.Commit();
                }
            }
            catch (Exception ex) 
            {
                throw ex;
            }
        }

        /// <summary>
        /// Get all mails(personal & customer related mails) for all user
        /// </summary>
        /// <returns></returns>
        public IEnumerable<MailDto> GetMailList(string customerNum, string customerName)
        {
            IEnumerable<MailDto> mails = null;

            if (string.IsNullOrWhiteSpace(customerNum) && string.IsNullOrWhiteSpace(customerName))
            {
                #region without customer related query condition
                mails = (from o in CommonRep.GetDbSet<MailTmp>()
                         where o.Deal == AppContext.Current.User.Deal
                             // && o.MailBox == AppContext.Current.User.Email
                         select new MailDto
                         {
                             From = o.From,
                             To = o.To,
                             Cc = o.Cc,
                             Subject = o.Subject,
                             Id = o.Id,
                             CreateTime = o.CreateTime,
                             UpdateTime = o.UpdateTime,
                             InternalDatetime = o.InternalDatetime,
                             Category = o.Category,
                             Type = o.Type,
                             MessageId = o.MessageId,
                             FileId = o.FileId,
                             CustomerMails = o.CustomerMails,
                             MailTime = o.Type == "IN" ? o.InternalDatetime : o.CreateTime,
                             MailBox = o.MailBox
                         }).OrderByDescending(m => m.CreateTime);
                #endregion
            }
            else
            {
                List<string> custs = new List<string>();
                if (!string.IsNullOrEmpty(customerNum))
                {
                    custs = customerNum.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
                }
                if (customerName == null)
                {
                    customerName = string.Empty;
                }

                CustomerService cs = SpringFactory.GetObjectImpl<CustomerService>("CustomerService");
                #region with customer related query condition
                mails = (from o in CommonRep.GetDbSet<MailTmp>()
                         where o.Deal == AppContext.Current.User.Deal
                             // && o.MailBox == AppContext.Current.User.Email
                         from oc in o.CustomerMails
                         join c in cs.GetCustomerMasterForCurrentUser() on new { oc.CustomerNum, o.Deal } equals new { c.CustomerNum, c.Deal }
                             into grps
                         where grps.Any(g => 
                             (custs.Contains(g.CustomerNum) || string.IsNullOrEmpty(customerNum))
                             && (g.CustomerName.IndexOf(customerName) >= 0 || string.IsNullOrEmpty(customerName)))
                         select new MailDto
                         {
                             From = o.From,
                             To = o.To,
                             Cc = o.Cc,
                             Subject = o.Subject,
                             Id = o.Id,
                             CreateTime = o.CreateTime,
                             UpdateTime = o.UpdateTime,
                             InternalDatetime = o.InternalDatetime,
                             Category = o.Category,
                             Type = o.Type,
                             MessageId = o.MessageId,
                             FileId = o.FileId,
                             CustomerMails = o.CustomerMails,
                             MailTime = o.Type == "IN" ? o.InternalDatetime : o.CreateTime,
                             Comments = o.From + " " + o.Subject + " " + o.CreateTime.ToString().Replace("T", " "),
                             MailBox = o.MailBox
                         }).OrderByDescending(m => m.CreateTime);
                #endregion
            }

            return mails;

            //CustomerService cs = SpringFactory.GetObjectImpl<CustomerService>("CustomerService");
            //var userCustomers = from c in cs.GetCustomerMasterForCurrentUser()
            //                    select c;

            //return (from o in CommonRep.GetDbSet<MailTmp>()
            //        let mc = userCustomers.Any(uc => o.CustomerMails.Any(cm => cm.CustomerNum == uc.CustomerNum))
            //        where o.Deal == AppContext.Current.User.Deal
            //            && ((o.MailBox == AppContext.Current.User.Email
            //            && o.CustomerMails.Count == 0)
            //            || (o.CustomerMails.Count > 0 && mc))
            //        select new MailDto
            //        {
            //            From = o.From,
            //            To = o.To,
            //            Cc = o.Cc,
            //            Subject = o.Subject,
            //            Id = o.Id,
            //            CreateTime = o.CreateTime,
            //            UpdateTime = o.UpdateTime,
            //            InternalDatetime = o.InternalDatetime,
            //            Status = o.CustomerMails.Count == 0
            //                       ? o.Status
            //                       : (mc && o.CustomerMails.Any(cm => cm.ProcessedFlg == "1")
            //                           ? "Processed"
            //                           : "Unprocessed"),
            //            Type = o.Type,
            //            MessageId = o.MessageId,
            //            FileId = o.FileId
            //        }).OrderByDescending(m => m.CreateTime);


        }

        public IEnumerable<CustomerMasterData> GetCustomerByMessageId(int mailId)
        {
            MailTmp mail = (from m in CommonRep.GetQueryable<MailTmp>().Include<MailTmp, ICollection<CustomerMail>>(m => m.CustomerMails)
                            where m.Id == mailId
                            select m).FirstOrDefault();
            List<string> customerNums = mail.CustomerMails.Select(o => o.CustomerNum).ToList<string>();

            return CommonRep.GetDbSet<CustomerMasterData>().Where(o => customerNums.Contains(o.CustomerNum));

        }

        public IEnumerable<CustomerMasterData> GetCustomers(string mailCustNums)
        {
            //MailTmp mail = (from m in CommonRep.GetQueryable<MailTmp>().Include<MailTmp, ICollection<CustomerMail>>(m => m.CustomerMails)
            //                             where m.Id == mailId
            //                             select m).FirstOrDefault();
            //List<string> customerNums = mail.CustomerMails.Select(o => o.CustomerNum).ToList<string>();
            List<string> customerNums = mailCustNums.Split(',').ToList<string>();

            return CommonRep.GetDbSet<CustomerMasterData>().Where(o => customerNums.Contains(o.CustomerNum));
                
        }

        public IEnumerable<InvoiceAging> GetInvoiceByMailId(string mailCustNumsForInv)
        {
            //MailTmp mail = (from m in CommonRep.GetQueryable<MailTmp>().Include<MailTmp, ICollection<CustomerMail>>(m => m.CustomerMails)
            //                where m.Id == mailIdForInv
            //                select m).FirstOrDefault();
            //List<string> customerNums = mail.CustomerMails.Select(o => o.CustomerNum).ToList<string>();
            List<string> customerNums = mailCustNumsForInv.Split(',').ToList<string>();
            return CommonRep.GetDbSet<InvoiceAging>().Where(m => m.Deal == AppContext.Current.User.Deal && customerNums.Contains(m.CustomerNum) && m.States != "004009");
        }

        public IEnumerable<InvoiceAging> GetInvoiceByInputNums(string mailCustNumsForInv, string inputNums)
        {
            List<string> customerNums = mailCustNumsForInv.Split(',').ToList<string>();
            List<string> invNums = inputNums.Split(',').ToList<string>();

            var tempList = CommonRep.GetDbSet<InvoiceAging>()
                        .Where(m => m.Deal == AppContext.Current.User.Deal
                            && customerNums.Contains(m.CustomerNum)
                            && m.States != "004009");

            //if (invNums.Count == 1) 
            //{
            //    return tempList.Where(m => m.InvoiceNum == invNums[0]);
            //}
            //else 
            //{
                return tempList.Where(m => invNums.Contains(m.InvoiceNum));
            //}
        }

        //public void RemoveCus(string messageId, string CusNum)
        //{
        //    var cus = CommonRep.GetDbSet<CustomerMail>().Where(o => o.MessageId == messageId && o.CustomerNum == CusNum);
        //    CommonRep.RemoveRange(cus);
        //    CommonRep.Commit();
        //}

        public void UpdateCusMails(string messageId, string CusNums)
        {
            var existing = CommonRep.GetDbSet<CustomerMail>().Where(cm => cm.MessageId == messageId);
            CommonRep.GetDbSet<CustomerMail>().RemoveRange(existing.AsEnumerable());
            CommonRep.Commit();

            if (!string.IsNullOrEmpty(CusNums))
            {
                List<string> customerNums = CusNums.Split(',').ToList<string>();
                

                List<CustomerMail> newAdds = new List<CustomerMail>();
                CustomerMail newCus = new CustomerMail();
                foreach (var item in customerNums)
                {
                    newCus = new CustomerMail();
                    newCus.CustomerNum = item;
                    newCus.MessageId = messageId;
                    newAdds.Add(newCus);
                }

                CommonRep.AddRange(newAdds);
                CommonRep.Commit();
            }

        }
    }
}
